#pragma once
#include <memory>
#include <sstream>
#include <istream>
#include <ostream>
#include <stdexcept>

template<class T>
class TTable {
public:
    TTable()
        : Rows()
        , Columns()
    {
    }

    TTable(size_t rows, size_t columns)
    {
        Init(rows, columns);
    }

    TTable(size_t rows, size_t columns, const T& initValue)
    {
        Init(rows, columns, initValue);
    }

    void Init(size_t rows, size_t columns) {
        Ptr.reset(new T[rows * columns]);
        Rows = rows;
        Columns = columns;
    }

    void Init(size_t rows, size_t columns, const T& initValue) {
        Init(rows, columns);

        // Инициализируем спец-значениями
        for (size_t i = 0, size = Rows * Columns; i < size; ++i)
            Ptr[i] = initValue;
    }

    size_t RowsCount() const {
        return Rows;
    }

    size_t ColumnsCount() const {
        return Columns;
    }

    T& operator()(size_t i, size_t j) {
        CheckBoundaries(i, j);
        return Ptr[i * Rows + j];
    }

    const T& operator()(size_t i, size_t j) const {
        CheckBoundaries(i, j);
        return Ptr[i * Rows + j];
    }

    friend std::ostream& operator<<(std::ostream& s, const TTable& t) {
        for (size_t i = 0; i < t.Rows; ++i) {
            for (size_t j = 0; j < t.Columns; ++j) {
                s << t(i, j) << "\t";
            }
            s << "\n";
        }
        return s;
    }

private:
    void CheckBoundaries(size_t i, size_t j) const {
        if (i >= Rows || j >= Columns) {
            std::ostringstream e;
            e << "Out of table range. Table is " << Rows << "x" << Columns << ". Requested [" << i << ", " << j << "] element";
            throw std::out_of_range(e.str());
        }
    }

private:
    std::unique_ptr<T[]> Ptr;
    size_t Rows, Columns;
};

template<class T>
std::istream& operator>>(std::itsream& str, TTable<T>& table) {
    size_t rows, columns;
    str >> rows >> columns;
    table.Init(rows, columns);
    for (size_t i = 0; i < rows; ++i)
        for (size_t j = 0; j < columns; ++j)
            str >> table(i, j);
    return str;
}

template<class T>
std::ostream& operator<<(std::ostream& str, const TTable<T>& table) {
    str << "\n";
    for (size_t i = 0, rows = table.RowsCount(); i < rows; ++i) {
        for (size_t j = 0, cols = table.ColumnsCount(); j < cols; ++j) {
            if (j)
                str << " ";
            str << table(i, j);
        }
        str << "\n";
    }
    return str;
}
